package com.example.administrator.exkotlin.base

import android.app.Activity
import android.content.Context
import android.content.Intent

/**
 * 高阶函数
 * 定义：一个函数接收另一个函数作为参数，或者返回值的那类型为另一个函数，那么该函数就成为高阶函数
 * 函数类型：添加函数类型到一个函数的参数申明或者返回值申明中，函数就成了高阶函数
 * 函数类型语法规则：
 *   (String ,Int)->Unit
 *   申明该函数接收什么参数（Kongzhi用()表示）-> 返回什么（Unit表示不返回）
 *
 *
 * 用途，高阶函数允许函数类型决定函数的执行逻辑，即使同一个高阶函数，都有可能在不同函数类型的参数条件下，返回不同结果
 *
 * 弊端：在字节码中，lambda表示被转换为匿名类的实现，因此每次调用都会增加额外的开销
 * 解决方案：使用内联函数，关键字 inline
 *
 *
 * 内联函数
 * 定义：在kotlin编译器中会将内联函数的代码在编译的时候自动替换到调用它的地方，这样就不存在运行时的额外开销
 *
 * 关键字 inline
 *
 * 排除内联函数：当一个高阶函数用内联函数关键字inline申明的时候，如果有多个函数类型参数，其中的某些参数不需要使用内联函数
 * 则可以使用noinline关键字来修饰，表示非内联函数
 *
 * inline内联函数所引用的lambda表达式可以使用return关键字来进行函数返回，并不会执行内联函数之后的内容
 * noinline非内联函数只能进行拒不返回return@MethodName 不再执行lambda表达式中的剩余代码，但是会执行高阶函数的之后的代码
 *
 * 如果在内敛高阶函数中的函数类型参数中已经使用了lambda表达式，则一定要用crossinlin来修饰该函数类型参数，避免return关键字冲突
 *
 * */

//例子
inline fun num1Andnum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    val result = operation(num1, num2)
    return result
}

fun add(num1: Int, num2: Int): Int {
    return num1 + num2
}

fun minus(num1: Int, num2: Int): Int {
    return num1 - num2
}

/**
 * 使用函数语法  ::MethodName
 * */
//调用上述
fun test() {

    //使用定义好的与参数类型匹配的函数
    val result1 = num1Andnum2(1, 2, ::add)
    val result2 = num1Andnum2(1, 2, ::minus)

    //lambda表达式
    val result3 = num1Andnum2(1, 2) { n1: Int, n2: Int ->
        n1 + n2
    }

    val result4 = num1Andnum2(1, 2) { n1, n2 ->
        n1 - n2
    }
    val result = StringBuilder().buildTest {
        append("1")
        append("2")
    }
    println("HightFunction")
    println(result1.toString())
    println(result2.toString())
    println(result3.toString())
    println(result4.toString())
    println(result.toString())
}

//练习，仿造一个apply标准函数
/**
 * 为StringBuiilder定义一个扩展函数
 * */
fun StringBuilder.buildTest(test: StringBuilder.() -> Unit): StringBuilder {
    test()
    //返回本身
    return this
}

//泛型作用在所有的类上，扩展函数
inline fun <T> T.buildTest(block: T.()->Unit):T{
    block()
    return this
}

fun Activity.start(context: Context, block: Intent.() -> Unit) {
    val intent = Intent(context, this::class.java)
    intent.block()
    context.startActivity(intent)
}
fun Activity.start(context: Context) {
    val intent = Intent(context, this::class.java)
    context.startActivity(intent)
}



